package org.yinxue.swing.unit.util;

import org.yinxue.swing.core.util.CloseUtil;
import org.yinxue.swing.core.util.LogUtil;
import org.yinxue.swing.unit.constant.LogPath;
import org.yinxue.swing.unit.constant.UnitConstant;
import org.yinxue.swing.unit.context.Context;
import org.yinxue.swing.unit.exception.JavaFileException;
import org.yinxue.swing.unit.filter.Filter;
import org.yinxue.swing.unit.model.ClassDesc;
import org.yinxue.swing.unit.model.JavaFile;

import java.io.*;
import java.util.ArrayList;
import java.util.LinkedList;
import java.util.List;
import java.util.Map;
import java.util.concurrent.Callable;
import java.util.concurrent.ExecutionException;
import java.util.concurrent.Future;

public class FileUtil {

    /**
     * 删除日志记录中生成的Test文件<br>
     *
     * @param path
     */
    public static int rollBack(String logPath) {
        String logContext = readJavaContext(logPath);
        String[] fileLines = logContext.split("\n");
        int count = 0;
        for (String line : fileLines) {
            if (!"".equals(line.trim())) {
                File file = new File(line);
                if (file.exists()) {
                    file.delete();
                    count++;
                }
            }
        }
        writeEmpty(logPath);
        return count;
    }

    /**
     * 从固定路径中读取java文件 <br>
     *
     * @param path
     * @return
     */
    public static String readJavaContext(String path) {
        File file = checkPath(path);
        FileReader fileReader = null;
        BufferedReader bufferedReader = null;
        StringBuilder sb = new StringBuilder(1024);
        try {
            String line = "";
            fileReader = new FileReader(file);
            bufferedReader = new BufferedReader(fileReader);
            while ((line = bufferedReader.readLine()) != null) {
                sb.append(line).append("\n");
            }
        } catch (Exception e) {
            LogUtil.error(FileUtil.class, "io error", e);
        } finally {
            CloseUtil.close(fileReader, bufferedReader);
        }
        return sb.toString();
    }

    /**
     * 向绝对路径文件写入空字符<br>
     *
     * @param logPath
     * @return
     */
    public static boolean writeEmpty(String logPath) {
        File file = checkPath(logPath);
        FileWriter fileWriter = null;
        try {
            fileWriter = new FileWriter(file);
            fileWriter.write("");
        } catch (IOException e) {
            LogUtil.error(FileUtil.class, "io error", e);
        } finally {
            CloseUtil.close(fileWriter);
        }
        return true;
    }


    /**
     * 对获得路径进行校验 <br>
     * TODO
     *
     * @return
     */
    private static File checkPath(String path) {
        return new File(path);
    }


    /**
     * 多线程读取文件夹下的java文件并且注册到结果中<br>
     * <pre>
     *     1. 获得父路径下的所有子路径
     *     2. 为自路径开启读取线程
     *     3. 阻塞运行，直到所有的文件读取完毕
     * <pre>
     *
     * @param absoluePath 绝对路径
     * @see
     */
    public static void batchReadFileByThread(String absolutePath, final Filter filter) {
        long now = System.currentTimeMillis();
        List<File> paths = new ArrayList<>();
        File file = checkPath(absolutePath);
        if (file.isHidden()) {
            file = file.getAbsoluteFile();
        }
        if (file.isDirectory()) {
            File[] childFiles = file.listFiles();
            if (null != childFiles) {
                for (File child : childFiles) {
                    if (child.isDirectory()) {
                        paths.add(child);
                        // 文件类型忽略
                    } else {

                    }
                }
            }
        }

        if (paths.size() == 0) {
            LogUtil.error(FileUtil.class, "未找到子文件", new Exception());
            return;
        }
        List<Future<Integer>> results = new ArrayList<>();
        for (final File path : paths) {
            Future<Integer> result = Context.threadPool().submitCallable(new Callable<Integer>() {
                @Override
                public Integer call() throws Exception {
                    try {
                        batchReadFile(path.getPath(), filter);
                        return 1;
                    } catch (Exception e) {
                        LogUtil.error(FileUtil.class, "线程处理失败:", e);
                        return 0;
                    }
                }
            });
            results.add(result);
        }

        if (results.size() == 0) {
            LogUtil.error(FileUtil.class, "异步线程结果为空", new Exception());
            return;
        }

        // 阻塞运行
        int count = 0;
        for (Future<Integer> result : results) {
            try {
                count += result.get();
            } catch (Exception e) {
                LogUtil.error(FileUtil.class, "异步阻塞线程处理异常", e);
            }
        }
        LogUtil.info(FileUtil.class, "多线程处理时间:{0}ms, 成功执行线程次数:{1}次", (System.currentTimeMillis() - now), count);
    }


    /**
     * 批量读取该文件夹下的java文件 <br>
     *
     * @param absolutePath
     * @return
     */
    public static void batchReadFile(String absolutePath, final Filter filter) throws JavaFileException {
        LinkedList<File> list = new LinkedList<>();
        File file = checkPath(absolutePath);
        if (file.isHidden()) {
            file = file.getAbsoluteFile();
        }
        if (file.isDirectory()) {
            File[] childFiles = file.listFiles();
            if (null != childFiles) {
                for (File child : childFiles) {
                    if (child.isDirectory()) {
                        list.add(child);
                    } else {
                        // 过滤出java文件
                        if (filter.isFile(child.getPath())) {
                            try {
                                JavaFile javaFile = new JavaFile(child);
                            } catch (Exception e) {
                                LogUtil.error(FileUtil.class, child.getPath(), e);
                            }
                        }
                    }
                }
            }
        }

        while (!list.isEmpty()) {
            final File dirFile = list.removeFirst();
            if (dirFile.isDirectory()) {
                batchReadFile(dirFile.getPath(), filter);
            }
        }
    }

    public static int batchCreateUnitContextFile() {
        long time = System.currentTimeMillis();
        int count = 0;
        // 有指定的类需要生成，不使用多线程批量处理
        String specClass = Context.config().getSpecClassDesc();
        if (specClass != null && !"".equals(specClass)) {
            String[] classDescs = specClass.split("\n");
            for (String className : classDescs) {
                if (className.contains("src/main/java")) {
                    // sonar上显示的项目相对路径,截取 com 到 .java的部分
                    className = className.substring(className.lastIndexOf("com"), className.lastIndexOf(".java"));
                    // 斜线置换为 .
                    className = className.replaceAll("/", ".");
                }
                JavaFile javaFile = Context.register().getJavaFile(className.trim());
                if (javaFile != null) {
                    javaFile.setUnitContext(javaFile.classDesc.buildUnitTestContext());
                    boolean flag = javaFile.createUnitFile();
                    if (flag) {
                        count++;
                    }
                }
            }
            return count;
        }
        Map<JavaFile, ClassDesc> map = Context.register().getCreateFileMap();

        class Result {
            int state;
            String classDescName;

            public Result(int state, String classDescName) {
                this.state = state;
                this.classDescName = classDescName;
            }
        }
        List<Future<Result>> results = new LinkedList<>();

        for (final Map.Entry<JavaFile, ClassDesc> entry : map.entrySet()) {
            Future<Result> result = Context.threadPool().submitCallable(new Callable<Result>() {
                @Override
                public Result call() throws Exception {
                    entry.getKey().unitContext = entry.getValue().buildUnitTestContext();
                    boolean isCreated = entry.getKey().createUnitFile();
                    if (isCreated) {
                        return new Result(1, entry.getKey().classDesc.simpleName);
                    } else {
                        return new Result(0, entry.getKey().classDesc.simpleName);
                    }
                }
            });
            results.add(result);
        }

        int fail = 0;
        List<String> failClassDesc = new ArrayList<>();
        for (Future<Result> result : results) {
            try {
                count += result.get().state;
            } catch (Exception e) {
                fail += 1;
                try {
                    failClassDesc.add(result.get().classDescName);
                } catch (InterruptedException e1) {
                    e1.printStackTrace();
                } catch (ExecutionException e1) {
                    e1.printStackTrace();
                }
                LogUtil.error(FileUtil.class, "异步线程阻塞异常,", e);
            }
        }
        LogUtil.info(FileUtil.class, "生成单元测试时间为:{0}ms, 返回成功的线程执行次数为:{1}次", System.currentTimeMillis() - time, count);
        LogUtil.error(FileUtil.class, "失败次数:{0}次，失败类：{1}", fail, failClassDesc.toString());
        return count;

    }

    public static boolean createUnitFile(String unitPath, String unitContext) {
        if (unitContext == null || unitContext.equals(UnitConstant.EMPTY)) {
            return false;
        }
        File file = new File(unitPath);
        if (file.exists()) {
            return false;
        }
        // 先创建路径
        String realPath = unitPath.substring(0, unitPath.lastIndexOf("\\"));
        File parent = new File(realPath);
        if (!parent.exists()) {
            parent.mkdirs();
        }
        FileOutputStream fos = null;
        OutputStreamWriter osw = null;
        BufferedWriter bufferedWriter = null;
        // 解决utf-8的写入问题
        try {
            fos = new FileOutputStream(file);
            LogUtils.writeLogFile(unitPath, LogPath.PARENT_PATH, LogPath.LIST_FILE);
            osw = new OutputStreamWriter(fos, "utf-8");
            bufferedWriter = new BufferedWriter(osw);
            bufferedWriter.write(unitContext);
            bufferedWriter.flush();
        } catch (Exception e) {
            LogUtil.error(FileUtil.class, "io error", e);
            return false;
        } finally {
            CloseUtil.close(fos, osw, bufferedWriter);
        }
        return true;
    }
}
